package com.atguigu.tingshu.search.service.impl;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.util.PinYinUtils;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import com.atguigu.tingshu.vo.album.TrackStatMqVo;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import cn.hutool.core.util.PageUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldSort;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.*;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
import co.elastic.clients.elasticsearch._types.query_dsl.TermsQueryField;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.*;
import co.elastic.clients.json.JsonData;
import com.atguigu.tingshu.album.client.CategoryFeignClient;
import com.atguigu.tingshu.model.album.*;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.repository.AlbumInfoIndexRepository;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexSearchVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.search.service.SearchService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;


@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class SearchServiceImpl implements SearchService {


    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;
    
    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private RedisTemplate<String , String> redisTemplate ;

    @Autowired
    private CategoryFeignClient categoryFeignClient;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private SuggestIndexRepository suggestIndexRepository ;

    @Autowired
    private AlbumInfoIndexRepository albumInfoIndexRepository ;

    @Autowired
    private ElasticsearchClient elasticsearchClient ;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;


    @Override
    public void upperAlbum(Long albumId)  {

        //准备albumInfoIndex封装对象数据
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();

        // 根据专辑的id查询专辑数据
        // 根据专辑的id查询专辑数据
        CompletableFuture<AlbumInfo> cf1 = CompletableFuture.supplyAsync(() -> {

            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(albumId);
            AlbumInfo albumInfo = albumInfoResult.getData();
            albumInfoIndex.setId(albumInfo.getId());
            albumInfoIndex.setAlbumTitle(albumInfo.getAlbumTitle());
            albumInfoIndex.setAlbumIntro(albumInfo.getAlbumIntro());
            albumInfoIndex.setCoverUrl(albumInfo.getCoverUrl());
            albumInfoIndex.setIncludeTrackCount(albumInfo.getIncludeTrackCount());
            albumInfoIndex.setIsFinished(String.valueOf(albumInfo.getIsFinished()));
            albumInfoIndex.setPayType(albumInfo.getPayType());
            albumInfoIndex.setCreateTime(albumInfo.getCreateTime());

            // 设置专辑的属性值数据
            List<AlbumAttributeValue> albumAttributeValueVoList = albumInfo.getAlbumAttributeValueVoList();
            List<AttributeValueIndex> attributeValueIndexList = albumAttributeValueVoList.stream().map(albumAttributeValue -> {
                AttributeValueIndex attributeValueIndex = new AttributeValueIndex();
                attributeValueIndex.setAttributeId(albumAttributeValue.getAttributeId());
                attributeValueIndex.setValueId(albumAttributeValue.getValueId());
                return attributeValueIndex;
            }).collect(Collectors.toList());
            albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);

            log.info("根据专辑的id查询专辑数据远程调用执行了....");

            return albumInfo;
        });

        // 根据三级分类的id查询分类数据
        CompletableFuture<Void> cf2 = cf1.thenAcceptAsync(dbAlbumInfo -> {
            Result<BaseCategoryView> categoryViewResult = categoryFeignClient.findCategoryByC3Id(dbAlbumInfo.getCategory3Id());
            BaseCategoryView baseCategoryView = categoryViewResult.getData();
            albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
            albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());
            albumInfoIndex.setCategory3Id(baseCategoryView.getCategory3Id());
            log.info("根据三级分类的id查询分类数据远程调用执行了....");
        });


        // 根据专辑的id查询专辑的统计数据
        CompletableFuture<Void> cf3 = CompletableFuture.runAsync(() -> {
            Result<AlbumStatVo> albumStatVoResult = albumInfoFeignClient.findAlbumStatByAlbumId(albumId);
            AlbumStatVo albumStatVo = albumStatVoResult.getData();
            albumInfoIndex.setPlayStatNum(albumStatVo.getPlayStatNum());
            albumInfoIndex.setSubscribeStatNum(albumStatVo.getSubscribeStatNum());
            albumInfoIndex.setBuyStatNum(albumStatVo.getBuyStatNum());
            albumInfoIndex.setCommentStatNum(albumStatVo.getCommentStatNum());
            double hotScore = albumInfoIndex.getSubscribeStatNum() * 0.2 +
                    albumInfoIndex.getBuyStatNum() * 0.3 +
                    albumInfoIndex.getCommentStatNum() * 0.4 +
                    albumInfoIndex.getPlayStatNum() * 0.1;
            albumInfoIndex.setHotScore(hotScore);
            log.info("根据专辑的id查询专辑的统计数据远程调用执行了....");
        });


        // 设置专辑的作者的名称
        CompletableFuture<Void> cf4 = cf1.thenAcceptAsync(dbAlbumInfo -> {
            Result<UserInfo> userInfoResult = userInfoFeignClient.findUserInfoById(dbAlbumInfo.getUserId());
            UserInfo userInfo = userInfoResult.getData();
            albumInfoIndex.setAnnouncerName(userInfo.getNickname());
            log.info("根据用户的id查询用户数据的远程调用执行了....");
        });

        /**
         * 主线程等待其他线程执行完毕以后再执行
         */
        CompletableFuture.allOf(cf1,cf2, cf3,cf4).join() ;
        log.info("albumInfoIndex: {}" , albumInfoIndex);

        // 把专辑数据保存到ES索引库中
        albumInfoIndexRepository.save(albumInfoIndex) ;

        // 保存专辑相关数据到ES的自动补全索引库中
        CompletableFuture.runAsync(()->{
            initSuggestIndex(albumInfoIndex);
        });

        // 把专辑的id存储到布隆过滤器中
        RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
        bloomFilter.add(albumId) ;
        log.info("向布隆过滤器中存储了专辑的id： {}" , albumId);

    }

    private void initSuggestIndex(AlbumInfoIndex albumInfoIndex) {
        //保存标题数据
        SuggestIndex albumTitleSuggestIndex = new SuggestIndex();
        String id = UUID.randomUUID().toString().replace("-","");
        albumTitleSuggestIndex.setId(id);
        albumTitleSuggestIndex.setTitle(albumInfoIndex.getAlbumTitle());
        albumTitleSuggestIndex.setKeyword(new Completion(new String[]{albumTitleSuggestIndex.getTitle()}));
        albumTitleSuggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAlbumTitle())}));  // PinYinUtils底层是对Pinying4J的api进行封装
        albumTitleSuggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAlbumTitle())})); //中文转拼音
        suggestIndexRepository.save(albumTitleSuggestIndex);

        //保存专辑的简介数据
        SuggestIndex suggestIndextro = new SuggestIndex();
        suggestIndextro.setId(UUID.randomUUID().toString().replace("-",""));
        suggestIndextro.setTitle(albumInfoIndex.getAlbumIntro());
        suggestIndextro.setKeyword(new Completion(new String[]{ albumInfoIndex.getAlbumIntro()}));
        suggestIndextro.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAlbumIntro())}));
        suggestIndextro.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAlbumIntro())}));
        suggestIndexRepository.save(suggestIndextro) ;

        //保存专辑的作者数据
        SuggestIndex announcerNameSuggestIndex = new SuggestIndex();
        announcerNameSuggestIndex.setId(UUID.randomUUID().toString().replace("-", ""));
        announcerNameSuggestIndex.setTitle(albumInfoIndex.getAnnouncerName());
        announcerNameSuggestIndex.setKeyword(new Completion(new String[]{ albumInfoIndex.getAnnouncerName()}));
        announcerNameSuggestIndex.setKeywordPinyin(new Completion(new String[]{ PinYinUtils.toHanyuPinyin(albumInfoIndex.getAnnouncerName())}) );   // PinYinUtils底层是对Pinying4J的api进行封装
        announcerNameSuggestIndex.setKeywordSequence(new Completion( new String[]{ PinYinUtils.getFirstLetter(albumInfoIndex.getAnnouncerName()) } ));
        suggestIndexRepository.save(announcerNameSuggestIndex) ;

    }

    @Override
    public void lowerAlbum(Long albumId) {
        albumInfoIndexRepository.deleteById(albumId);
        log.info("从es索引库中删除了albumId为：{} 专辑数据" , albumId);

        // 对布隆过滤器进行重建
        RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER_NEW);
        bloomFilter.tryInit(100000 , 0.000001) ;
        
        //创建一个新的布隆过滤器
        Iterable<AlbumInfoIndex> albumInfoIndices = albumInfoIndexRepository.findAll();
        albumInfoIndices.forEach(albumInfoIndex -> {
            Long esAlbumId = albumInfoIndex.getId();
            bloomFilter.add(esAlbumId) ;
        });
        log.info("重建布隆过滤器成功");

        // 2、删除之前的布隆过滤器， 对新的布隆过滤器进行重命名
        String script = "redis.call('del' , KEYS[1])\n" +
                "redis.call('del' , \"{\" .. KEYS[1] .. \"}:config\")\n" +
                "redis.call('rename' , KEYS[2] , KEYS[1])\n" +
                "redis.call('rename' , \"{\" .. KEYS[2] .. \"}:config\" , \"{\" .. KEYS[1] .. \"}:config\")\n" +
                "return 1 " ;

        // RedisScript<T> script: 封装lua脚本
        // List<K> keys: 传递给lua脚本的key的集合
        // Object... args: 传递给lua脚本的参数的可变数组，后期lua脚本就可以通过ARGV[索引]从参数数组中获取参数
        Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(RedisConstant.ALBUM_BLOOM_FILTER, RedisConstant.ALBUM_BLOOM_FILTER_NEW));
        if(result == 1) {
            log.info("新的布隆过滤器重命名成功了....");
        }

    }

    //专辑列表的查询
    @Override
    public AlbumSearchResponseVo search(AlbumIndexQuery albumIndexQuery) throws IOException {

        // 创建SearchRequest对象
        SearchRequest searchRequest = buildSearchRequest(albumIndexQuery);

        // 使用ElasticSearchClient的方法完成高级搜索
        SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);

        // 从SearchResponse对象解析响应结果数据
        AlbumSearchResponseVo albumSearchResponseVo = parseSearchResult(searchResponse , albumIndexQuery) ;

        // 返回解析结果
        return albumSearchResponseVo;
    }

    @Override
    public List<AlbumInfoIndexSearchVo> channel(Long category1Id) throws IOException {

        // 根据一级分类的id查找置顶的三级分类数据
        Result<List<BaseCategory3>> result = categoryFeignClient.findTopBaseCategory3(category1Id);
        List<BaseCategory3> baseCategory3List = result.getData();

        // 创建TermsQueryField对象
        List<FieldValue> fieldValueList = baseCategory3List.stream().map(baseCategory3 -> {
            Long id = baseCategory3.getId();
            FieldValue fieldValue = new FieldValue.Builder().longValue(id).build();
            return fieldValue;
        }).collect(Collectors.toList());
        TermsQueryField termsQueryField = new TermsQueryField.Builder()
                .value(fieldValueList)
                .build();

        // 创建一个BoolQuery对象
        BoolQuery boolQuery = QueryBuilders.bool()
                .must(builder -> builder.term(QueryBuilders.term().field("category1Id").value(category1Id).build()))
                .must(builder -> builder.terms(QueryBuilders.terms().field("category3Id").terms(termsQueryField).build()))
                .build();
        Query query = new Query.Builder()
                .bool(boolQuery)
                .build() ;

        // 创建Aggregation对象
        SortOptions sortOptions = new SortOptions.Builder().field(builder -> builder.field("hotScore").order(SortOrder.Desc)).build() ;
        Aggregation albumInfoAggregation = new Aggregation.Builder().topHits(builder -> builder.size(6).sort(sortOptions)).build();

        // 创建一个Aggregation对象
        Aggregation aggregation = new Aggregation.Builder()
                .terms(builder -> builder.field("category3Id")
                        .size(10))
                .aggregations("albumInfoAgg" , albumInfoAggregation)
                .build();

        // 创建SearchRequest对象
        SourceConfig sourceConfig  = new SourceConfig.Builder().fetch(false).build() ;
        SearchRequest searchRequest = new SearchRequest.Builder()
                .index("albuminfo")
                .query(query)
                .source(sourceConfig)
                .aggregations("category3IdAgg" , aggregation)
                .build();

        // 执行搜索
        SearchResponse<AlbumInfoIndex> indexSearchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);

        Map<String, Aggregate> aggregations = indexSearchResponse.aggregations();
        Aggregate aggregate = aggregations.get("category3IdAgg");
        LongTermsAggregate longTermsAggregate = (LongTermsAggregate)aggregate._get() ;
        Buckets<LongTermsBucket> bucketBuckets = longTermsAggregate.buckets();
        List<LongTermsBucket> longTermsBuckets = bucketBuckets.array();

        List<AlbumInfoIndexSearchVo> albumInfoIndexSearchVos = new ArrayList<>() ;
        for(LongTermsBucket longTermsBucket:longTermsBuckets ) {

            AlbumInfoIndexSearchVo albumInfoIndexSearchVo = new AlbumInfoIndexSearchVo() ;

            // 获取三级分类的id
            long c3Id = longTermsBucket.key();
            BaseCategory3 dbBaseCategory3 = baseCategory3List.stream().filter(baseCategory3 -> baseCategory3.getId() == c3Id)
                    .collect(Collectors.toList()).get(0);

            Map<String, Aggregate> aggregateMap = longTermsBucket.aggregations();
            Aggregate albumInfoAgg = aggregateMap.get("albumInfoAgg");
            TopHitsAggregate topHitsAggregate = (TopHitsAggregate) albumInfoAgg._get();
            HitsMetadata<JsonData> hitsMetadata = topHitsAggregate.hits();
            List<Hit<JsonData>> hits = hitsMetadata.hits();

            // 获取文档数据
            List<AlbumInfoIndex> albumInfoIndexList = hits.stream().map(hit -> {
                JsonData jsonData = hit.source();
                AlbumInfoIndex albumInfoIndex = jsonData.to(AlbumInfoIndex.class);
                return albumInfoIndex;
            }).collect(Collectors.toList());

            albumInfoIndexSearchVo.setBaseCategory3(dbBaseCategory3);
            albumInfoIndexSearchVo.setList(albumInfoIndexList);

            albumInfoIndexSearchVos.add(albumInfoIndexSearchVo);

        }

        return albumInfoIndexSearchVos;
    }
    @SneakyThrows
    @Override
    public List<String> completeSuggest(String keyword) {

        CompletionSuggester  keywordCompletionSuggester = new CompletionSuggester.Builder()
                .field("keyword")
                .skipDuplicates(true)
                .size(10)
                .fuzzy(fuzzyBuilder -> fuzzyBuilder.fuzziness("auto"))
                .build();

        CompletionSuggester keywordPinyinCompletionSuggester = new CompletionSuggester.Builder()
                .field("keywordPinyin")
                .skipDuplicates(true)
                .size(10)
                .fuzzy(fuzzyBuilder-> fuzzyBuilder.fuzziness("auto"))
                .build();


        CompletionSuggester keywordSequenceCompletionSuggester = new CompletionSuggester.Builder()
                .field("keywordSequence")
                .skipDuplicates(true)
                .size(10)
                .fuzzy(fuzzyBuilder -> fuzzyBuilder.fuzziness("auto"))
                .build();

        Suggester suggester = new Suggester.Builder()
                .suggesters("suggestionKeyword" , builder -> builder.completion(keywordCompletionSuggester).prefix(keyword))
                .suggesters("suggestionKeywordPinyin" , builder -> builder.completion(keywordPinyinCompletionSuggester).prefix(keyword))
                .suggesters("suggestionKeywordSequence" , builder -> builder.completion(keywordSequenceCompletionSuggester).prefix(keyword))
                .build() ;

        //创建一个SearchRequest对象
        SearchRequest searchRequest = new SearchRequest.Builder()
                .index("suggestinfo")
                .suggest(suggester)
                .build() ;

        //使用es对象实现自动补全查询
        SearchResponse<SuggestIndex> searchResponse = elasticsearchClient.search(searchRequest, SuggestIndex.class);

        Map<String, List<Suggestion<SuggestIndex>>> suggestMap = searchResponse.suggest();

        //因为options是一个集合 要想整体返回 就要创建一个list集合
        List<String> result = new ArrayList<>();
        getSuggestResult(result , suggestMap , "suggestionKeyword") ;
        getSuggestResult(result , suggestMap , "suggestionKeywordPinyin") ;
        getSuggestResult(result , suggestMap , "suggestionKeywordSequence") ;


        return result;
    }

    @SneakyThrows
    @Override
    public void updateLatelyAlbumRanking() {
        //创建一个Query对象
        Query query = new Query.Builder()
                .matchAll(QueryBuilders.matchAll().build())
                .build();

        // 创建一个SourceConfig对象
        SourceConfig sourceConfig = new SourceConfig.Builder().fetch(false).build() ;

        // 创建一个Aggregation对象
        Aggregation.Builder.ContainerBuilder containerBuilder = new Aggregation.Builder().terms(builder -> builder.field("category1Id").size(40));

        String[] rankingDimensionArray = new String[]{ "hotScore" , "playStatNum" , "subscribeStatNum" , "buyStatNum", "commentStatNum"} ;
        for(String rankingDimension : rankingDimensionArray) {
            SortOptions sortOptions = new SortOptions.Builder()
                    .field(builder -> builder.field(rankingDimension).order(SortOrder.Desc))
                    .build();
            Aggregation aggregation = new Aggregation.Builder().topHits(builder -> builder.size(10).sort(sortOptions)).build();
            containerBuilder.aggregations(rankingDimension + "TopHitAgg" , aggregation) ;
        }

        // 从ES中进行统计查询
        SearchRequest searchRequest = new SearchRequest.Builder()
                .index("albuminfo")
                .query(query)
                .source(sourceConfig)
                .aggregations("category1IdAgg" , containerBuilder.build())
                .build() ;

        SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);

        // 将统计查询的结果数据存储到Redis
        Map<String, Aggregate> aggregations = searchResponse.aggregations();
        Aggregate aggregate = aggregations.get("category1IdAgg");
        Object o = aggregate._get();
        //在这里进行断点调试 找到他的类型
        LongTermsAggregate longTermsAggregate = (LongTermsAggregate) aggregate._get();
        Buckets<LongTermsBucket> buckets = longTermsAggregate.buckets();
        List<LongTermsBucket> termsBuckets = buckets.array(); //桶对象转换成一个集合

        for(LongTermsBucket termsBucket : termsBuckets) {
            long category1Id = termsBucket.key();       // 获取一级分类的id
            String rankingRedisKey = RedisConstant.RANKING_KEY_PREFIX + category1Id ;

            Map<String, Aggregate> aggregateMap = termsBucket.aggregations();
            //Aggregate aggregate1 = aggregateMap.get();
            //在这里打断点
            for(String rankingDimension : rankingDimensionArray) {
                Aggregate agg = aggregateMap.get(rankingDimension + "TopHitAgg");
                TopHitsAggregate topHitsAggregate = (TopHitsAggregate) agg._get();
                HitsMetadata<JsonData> hitsMetadata = topHitsAggregate.hits();
                List<Hit<JsonData>> hits = hitsMetadata.hits();
                List<AlbumInfoIndex> albumInfoIndexList = new ArrayList<>() ;
                for(Hit<JsonData> hit : hits) {
                    AlbumInfoIndex albumInfoIndex = hit.source().to(AlbumInfoIndex.class);
                    albumInfoIndexList.add(albumInfoIndex) ;
                }

                // 把数据统计到结果数据存储到Redis中
                /**
                 * 应该使用Redis中哪一种数据类型完成数据的存储？
                 * string , List , set , hash , zset
                 *
                 * 后期在进行查询的时候是需要按照一级分类的id以及统计维度的字符串进行查询， hash
                 * 使用hash结果进行存储，针对每一个分类只需要再Redis中形成1个key
                 */

                redisTemplate.opsForHash().put(rankingRedisKey , rankingDimension , JSON.toJSONString(albumInfoIndexList));
            }

        }
    }

    @Override
    public List<AlbumInfoIndex> findRankingList(Long category1Id, String dimension) { //dimension统计维度的字符串
        // 从Redis中获取统计结果数据
        String rankingRedisKey = RedisConstant.RANKING_KEY_PREFIX + category1Id ;
        Object obj = redisTemplate.opsForHash().get(rankingRedisKey, dimension);
        if(obj != null) {
            String jsonString = obj.toString();
            List<AlbumInfoIndex> albumInfoIndexList = JSON.parseArray(jsonString, AlbumInfoIndex.class);
            return albumInfoIndexList ;
        }else {
            return new ArrayList<>() ;
        }
    }

    @Override
    public void albumStatUpdate(TrackStatMqVo trackStatMqVo) {

        Long albumId = trackStatMqVo.getAlbumId();
        Optional<AlbumInfoIndex> optional = albumInfoIndexRepository.findById(albumId);
        AlbumInfoIndex albumInfoIndex = optional.get();

        String statType = trackStatMqVo.getStatType();
        switch (statType) {
            case SystemConstant.ALBUM_STAT_PLAY:
                albumInfoIndex.setPlayStatNum(trackStatMqVo.getCount());
                break;
            case SystemConstant.ALBUM_STAT_SUBSCRIBE:
                albumInfoIndex.setSubscribeStatNum(trackStatMqVo.getCount());
                break;
            case SystemConstant.ALBUM_STAT_BROWSE:
                albumInfoIndex.setBuyStatNum(trackStatMqVo.getCount());
                break;
            case SystemConstant.ALBUM_STAT_COMMENT:
                albumInfoIndex.setCommentStatNum(trackStatMqVo.getCount());
                break;
        }

        albumInfoIndexRepository.save(albumInfoIndex) ;
        log.info("专辑的统计数据同步完毕.......");
    }

    public void getSuggestResult(List<String> result , Map<String, List<Suggestion<SuggestIndex>>> suggestMap , String suggestFieldName) {
        List<Suggestion<SuggestIndex>> suggestionList = suggestMap.get(suggestFieldName);

        for(Suggestion<SuggestIndex> suggestion : suggestionList ){
            CompletionSuggest<SuggestIndex> completion = suggestion.completion();
            List<CompletionSuggestOption<SuggestIndex>> options = completion.options();
            options.stream().forEach(suggestoptin->{
                SuggestIndex suggestIndex = suggestoptin.source();
                result.add(suggestIndex.getTitle());
            });


        }


    }

    private SearchRequest buildSearchRequest(AlbumIndexQuery albumIndexQuery) {
        //  创建BoolQuery对象
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();

        // 拼接KeyWord的搜索参数
        String keyword = albumIndexQuery.getKeyword();
        if(!StringUtils.isEmpty(keyword)) {
            Query query = QueryBuilders.multiMatch(builder -> builder.fields("albumTitle", "albumIntro").query(keyword));
            boolQueryBuilder.must(query) ;
        }else {
            Query query = QueryBuilders.matchAll().build()._toQuery();
            boolQueryBuilder.must(query) ;
        }

        // 分类搜索参数的拼接
        Long category1Id = albumIndexQuery.getCategory1Id();
        if(category1Id != null) {
            Query query = QueryBuilders.term(builder -> builder.field("category1Id").value(category1Id));
            boolQueryBuilder.filter(query) ;
        }

        Long category2Id = albumIndexQuery.getCategory2Id();
        if(category2Id != null) {
            Query query = QueryBuilders.term(builder -> builder.field("category2Id").value(category2Id));
            boolQueryBuilder.filter(query) ;
        }

        Long category3Id = albumIndexQuery.getCategory3Id();
        if(category3Id != null) {
            Query query = QueryBuilders.term(builder -> builder.field("category3Id").value(category3Id));
            boolQueryBuilder.filter(query) ;
        }

        // 拼接专辑搜索的搜索条件
        List<String> attributeList = albumIndexQuery.getAttributeList();
        if(!CollectionUtils.isEmpty(attributeList)) {
            for(String attributeAndValue : attributeList) {
                String[] split = attributeAndValue.split(":");
                String atttrId = split[0] ;
                String attrValue = split[1] ;

                BoolQuery boolQuery = QueryBuilders.bool()
                        .must(QueryBuilders.term(builder -> builder.field("attributeValueIndexList.attributeId").value(atttrId)))
                        .must(QueryBuilders.term(builder -> builder.field("attributeValueIndexList.valueId").value(attrValue)))
                        .build();

                Query query = QueryBuilders.nested(builder ->
                        builder.path("attributeValueIndexList")
                                .query(boolQuery._toQuery()));

                boolQueryBuilder.filter(query) ;
            }
        }

        // 创建一个Query对象，封装请求参数
        Query query = new Query.Builder()
                .bool(boolQueryBuilder.build())
                .build() ;

        // 创建一个SearchRquestBuilder对象
        SearchRequest.Builder builder = new SearchRequest.Builder();
        builder.index("albuminfo") ;                // 给builder对象设置索引库的名称
        builder.query(query) ;

        String orderInfo = albumIndexQuery.getOrder();
        FieldSort.Builder fieldSortBuilder = new FieldSort.Builder() ;      // 创建FieldSort对象

        if(!StringUtils.isEmpty(orderInfo)) {

            String[] split = orderInfo.split(":");
            String sortField = split[0] ;
            String order = split[1] ;

            switch (sortField) {
                case "1" :
                    fieldSortBuilder.field("hotScore").order("asc".equalsIgnoreCase(order) ? SortOrder.Asc : SortOrder.Desc) ;
                    break;
                case "2" :
                    fieldSortBuilder.field("playStatNum").order("asc".equalsIgnoreCase(order) ? SortOrder.Asc : SortOrder.Desc) ;
                    break;
                case "3" :
                    fieldSortBuilder.field("createTime").order("asc".equalsIgnoreCase(order) ? SortOrder.Asc : SortOrder.Desc) ;
                    break;
            }

        }else {
            fieldSortBuilder.field("hotScore").order(SortOrder.Desc) ;
        }

        // 创建SortOptions对象
        SortOptions sortOptions = new SortOptions.Builder()
                .field(fieldSortBuilder.build())
                .build();
        builder.sort(sortOptions) ;

        // 设置分页查询
        builder.from((albumIndexQuery.getPageNo() - 1) * albumIndexQuery.getPageSize()).size(albumIndexQuery.getPageSize()) ;

        // 设置高亮的参数
        if(!StringUtils.isEmpty(keyword)) {
            Highlight highlight = new Highlight.Builder()
                    .fields("albumTitle" , highlightBuilder -> highlightBuilder.preTags("<font color='red'>").postTags("</font>"))
                    .fields("albumIntro" , highlightBuilder -> highlightBuilder.preTags("<font color='red'>").postTags("</font>"))
                    .build() ;
            builder.highlight(highlight) ;
        }

        SearchRequest searchRequest = builder.build();
        log.info("dsl: {}" , searchRequest.toString());

        return searchRequest ;
    }

    private AlbumSearchResponseVo parseSearchResult(SearchResponse<AlbumInfoIndex> searchResponse, AlbumIndexQuery albumIndexQuery) {

        HitsMetadata<AlbumInfoIndex> hitsMetadata = searchResponse.hits();

        List<Hit<AlbumInfoIndex>> hits = hitsMetadata.hits();
        List<AlbumInfoIndexVo> albumInfoIndexVoList = hits.stream().map(hit -> {
            AlbumInfoIndex albumInfoIndex = hit.source();
            AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
            BeanUtils.copyProperties(albumInfoIndex , albumInfoIndexVo);

            // 从搜索结果中获取高量的结果，使用高亮的结果替换原始文档中的数据
            Map<String, List<String>> highlight = hit.highlight();
            if(!CollectionUtils.isEmpty(highlight)) {

                List<String> albumTitleHighlightList = highlight.get("albumTitle");
                if(!CollectionUtils.isEmpty(albumTitleHighlightList)) {
                    String albumTitleHighlight = albumTitleHighlightList.get(0) ;
                    albumInfoIndexVo.setAlbumTitle(albumTitleHighlight);
                }

                List<String> albumIntroHighlightList = highlight.get("albumIntro");
                if(!CollectionUtils.isEmpty(albumIntroHighlightList)) {
                    String albumIntroHighlight = albumIntroHighlightList.get(0) ;
                    albumInfoIndexVo.setAlbumIntro(albumIntroHighlight);
                }

            }

            return albumInfoIndexVo;
        }).collect(Collectors.toList());

        // 创建AlbumSearchResponseVo对象，封装搜索结果数据
        AlbumSearchResponseVo albumSearchResponseVo = new  AlbumSearchResponseVo() ;
        albumSearchResponseVo.setList(albumInfoIndexVoList);

        // 分页相关参数的设定
        long total = hitsMetadata.total().value();
        albumSearchResponseVo.setTotal(total);
        albumSearchResponseVo.setPageSize(albumIndexQuery.getPageSize());
        albumSearchResponseVo.setPageNo(albumIndexQuery.getPageNo());
        Integer totalPage = PageUtil.totalPage((int) total, albumIndexQuery.getPageSize());
        albumSearchResponseVo.setTotalPages(Long.parseLong(totalPage.toString()));

        return albumSearchResponseVo ;
    }
}

